/*
 * Copyright (c) 2016 Advanced Micro Devices, Inc.
 * All rights reserved.
 *
 * For use for simulation and test purposes only
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Author: Matthew Poremba
 */

#ifndef __MEM_TOKEN_PORT_HH__
#define __MEM_TOKEN_PORT_HH__

#include "mem/port.hh"

class TokenManager;
class TokenSlavePort;

class TokenMasterPort : public MasterPort
{
  protected:
    TokenManager *tokenManager;
    TokenSlavePort *_tokenSlavePort;

  public:
    TokenMasterPort(const std::string& name, MemObject* owner,
                    PortID id = InvalidPortID) :
        MasterPort(name, owner, id), tokenManager(nullptr),
        _tokenSlavePort(nullptr)
    { }

    virtual void recvTokens(int num_tokens);
    virtual bool haveTokens(int num_tokens);
    virtual void acquireTokens(int num_tokens);

    virtual void setTokenManager(TokenManager *_tokenManager);

    void bind(BaseSlavePort& slave_port);
};

class TokenSlavePort : public SlavePort
{
  protected:
    TokenMasterPort *_tokenMasterPort;

    std::deque<PacketPtr> respQueue;

    void recvRespRetry();

  public:
    TokenSlavePort(const std::string& name, MemObject *owner,
                   PortID id = InvalidPortID) :
        SlavePort(name, owner, id), _tokenMasterPort(nullptr)
    { }

    virtual ~TokenSlavePort() { }

    virtual void sendTokens(int num_tokens);

    void bind(MasterPort& master_port);

    bool sendTimingResp(PacketPtr pkt);
};

class TokenManager
{
  protected:
    int maxTokens;
    int availableTokens;

  public:
    TokenManager(int init_tokens);
    virtual ~TokenManager() { }

    int getMaxTokenCount() const;

    virtual void recvTokens(int num_tokens);
    virtual bool haveTokens(int num_tokens);
    virtual void acquireTokens(int num_tokens);
};

#endif
